Skip to content

Fix open issues: security, routing, and reliability (#17-#27)#28

Merged
OnlyTerp merged 1 commit into
mainfrom
devin/1781763007-fix-open-issues
Jun 18, 2026
Merged

Fix open issues: security, routing, and reliability (#17-#27)#28
OnlyTerp merged 1 commit into
mainfrom
devin/1781763007-fix-open-issues

Conversation

@OnlyTerp

@OnlyTerp OnlyTerp commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves the open issues in the tracker. Each is a focused, stdlib-only fix; no new dependencies. The offline self-test, compileall, doctor --no-test --ci, auto_router_demo, and the new ASCII guard all pass locally, and test_proxy.py now exercises every fix below.

#14 (long-context openai_compat hygiene: tool-only content=null + context-window hint) is already resolved in main and is covered by the existing openai_compat long-context hygiene test — no code change needed here.

Security

  • #19 config leak / unauthenticated routing mutation/healthz exposed the full backend config and /uc/select let any local web page flip routing. When bound to loopback (the default), every endpoint now requires a loopback Host header (_guard_local() → 403 otherwise). A browser can't forge Host, so this blocks DNS-rebinding. Opt out by binding a non-loopback UC_LISTEN_HOST.

  • #22 credential leak / SSRF to custom upstreams — Claude Code's own inbound Anthropic credential was forwarded to any route upstream. Now inbound creds (authorization, x-api-key) are stripped before dispatch to a non-default upstream unless the route explicitly opts in with auth: "passthrough".

    • Found and fixed a real bug while writing the test: _routes_to_slots dropped the "passthrough" intent entirely (if auth and auth != "passthrough"), so the opt-in could never re-enable forwarding. The intent is now preserved as slot["auth_passthrough"] and honored at dispatch:
      # _routes_to_slots
      if auth == "passthrough": slot["auth_passthrough"] = True
      elif auth:               slot["auth"] = _expand_env(auth)
  • #23 prompt injection in cursor_agent — the <CLAUDE_TOOL_CALL>{...}</CLAUDE_TOOL_CALL> bridge marker is our private control channel, but it was parsed out of cursor-agent output after untrusted transcript content (user text, tool results) could smuggle one in. _flatten_messages now defangs the tag in all rendered transcript content, so only our own trailing instructions can emit a real call.

  • #25 settings TOCTOU / world-readable ANTHROPIC_BASE_URL — launchers now create the state dir private (chmod 700 / icacls lock to the current user) and write session settings via a umask 077 temp file + chmod 600 + atomic mv, so another local account can't read or symlink-race the ANTHROPIC_BASE_URL.

  • #21 supply-chain integrity — CI actions are pinned to commit SHAs (actions/checkout v4.3.1, actions/setup-python v5.6.0) and a new scripts/check_ascii_ps1.py CI step fails the build on non-ASCII bytes in any .ps1 (regression guard for #17).

  • #27 unverified JWT — clarified that the Codex JWT decode is best-effort/unverified and used only for a cosmetic account-id/exp routing hint (never an auth decision; authority is enforced upstream). Also switched REFRESH_CMD.split()shlex.split(..., posix=os.name != "nt") so a refresh command with a quoted path isn't split on internal spaces.

Reliability / correctness

  • #24 resource exhaustion — added a request body cap (UC_MAX_BODY_BYTES, default 64 MiB) that returns 413 before allocating, a BoundedSemaphore(UC_MAX_CONNECTIONS) so a connection flood applies back-pressure instead of spawning unbounded threads, and a per-socket timeout (UC_SOCKET_TIMEOUT) to bound idle/slowloris connections.

  • #20 non-deterministic router cache — the cache key used Python's builtin hash(), which is salted per process (PYTHONHASHSEED), so the same task minted a different key every restart and silently re-ran the classifier. Replaced with a stable, tier-scoped SHA256 digest: "%s|%s" % (tier, sha256(task)[:32]).

  • #17 PowerShell 5.1 parse failureStart-UltraCode.ps1 had an em-dash (U+2014) on line 233; PS 5.1 decodes a BOM-less .ps1 as CP1252, where 0x94 is a closing quote and broke string parsing. Replaced with ASCII --.

  • #18 workflows fall back to stock Claude — the orchestrator/worker pick lived only in memory, so a proxy restart (or a workflow spawning a fresh proxy) forgot it and sub-agents reverted to stock Claude. The pick now persists to UC_SELECTION_CACHE (set by both launchers) and is restored on startup; a one-time diagnostic surfaces when selection is on but empty so a lost pick isn't mistaken for the workflow ignoring the model.

Tests (#26)

test_proxy.py previously covered only the mock-based core. Added offline coverage for the loopback guard (403 on non-local Host), the oversize-body 413, credential stripping vs. auth:passthrough opt-in, deterministic router cache keys, selection persist/restore across a simulated restart, the cursor_agent injection defang, and best-effort JWT decoding. The provider modules' built-in self-tests gained an injection-guard assertion as well.

Link to Devin session: https://app.devin.ai/sessions/b788d729b84747d094cdff871cb52d4b
Requested by: @OnlyTerp


Open in Devin Review

- #17: replace em-dash with ASCII in Start-UltraCode.ps1 (PS 5.1 CP1252 decode
  broke string parsing on BOM-less .ps1)
- #21: pin CI actions to commit SHAs; add non-ASCII .ps1 CI guard
- #20: stable SHA256 router cache key (builtin hash() is salted per process, so
  the cache silently missed every restart)
- #19: loopback Host-header guard on all endpoints (anti DNS-rebinding)
- #22: strip inbound Anthropic creds when forwarding to a custom upstream unless
  the route opts in with auth:passthrough; fix passthrough intent being dropped
  during slot construction so the opt-in actually forwards the credential
- #23: defang injected <CLAUDE_TOOL_CALL> markers in untrusted transcript
- #24: request body cap (413), bounded handler threads, per-socket timeout
- #25: launcher state dir 0700 + atomic 0600 session-settings write
- #27: shlex.split(REFRESH_CMD) for quoted paths; document JWT decode as
  best-effort/unverified
- #18: persist orchestrator/worker selection across proxy restarts via
  UC_SELECTION_CACHE; surface lost-selection fallback
- #26: add offline coverage for the above

All offline checks pass: test_proxy.py, compileall, doctor --no-test --ci,
auto_router_demo, check_ascii_ps1.

Co-Authored-By: Rob <onerobby@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@OnlyTerp OnlyTerp merged commit dc0c4eb into main Jun 18, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant